{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import hashlib\n",
    "from datetime import datetime\n",
    "\n",
    "\n",
    "class Block:\n",
    "    \"\"\"\n",
    "        区块结构\n",
    "            prev_hash:      父区块哈希值\n",
    "            data:           区块内容\n",
    "            timestamp:      区块创建时间\n",
    "            hash:           区块哈希值\n",
    "            Nonce:        随机数\n",
    "    \"\"\"\n",
    "    def __init__(self, data, prev_hash):\n",
    "        # 将传入的父哈希值和数据保存到类变量中\n",
    "        self.prev_hash = prev_hash    \n",
    "        self.data = data\n",
    "        # 获取当前时间\n",
    "        self.timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n",
    "        \n",
    "        # 设置Nonce和哈希的初始值为None\n",
    "        self.nonce = None\n",
    "        self.hash = None\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return \"区块内容：%s\\n哈希值: %s\" % (self.data, self.hash)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ProofOfWork:\n",
    "    \"\"\"\n",
    "        工作量证明\n",
    "    \"\"\"\n",
    "    def __init__(self, block, difficult=5):\n",
    "        self.block = block\n",
    "        \n",
    "        # 定义工作量难度，默认为5，表示有效的哈希值以5个“0”开头\n",
    "        self.difficulty = difficult\n",
    "\n",
    "    def mine(self):\n",
    "        \"\"\"\n",
    "            挖矿函数\n",
    "        \"\"\"\n",
    "        i = 0\n",
    "        prefix = '0' * self.difficulty\n",
    "\n",
    "        while True:\n",
    "            message = hashlib.sha256()\n",
    "            message.update(str(self.block.prev_hash).encode('utf-8'))\n",
    "            message.update(str(self.block.data).encode('utf-8'))\n",
    "            message.update(str(self.block.timestamp).encode('utf-8'))\n",
    "            message.update(str(i).encode(\"utf-8\"))\n",
    "            digest = message.hexdigest()\n",
    "            if digest.startswith(prefix):\n",
    "                self.block.nonce = i\n",
    "                self.block.hash = digest\n",
    "                return self.block\n",
    "            i += 1\n",
    "\n",
    "    def validate(self):\n",
    "        \"\"\"\n",
    "            验证有效性\n",
    "        \"\"\"\n",
    "        message = hashlib.sha256()\n",
    "        message.update(str(self.block.prev_hash).encode('utf-8'))\n",
    "        message.update(str(self.block.data).encode('utf-8'))\n",
    "        message.update(str(self.block.timestamp).encode('utf-8'))\n",
    "        message.update(str(self.block.nonce).encode('utf-8'))\n",
    "        digest = message.hexdigest()\n",
    "\n",
    "        prefix = '0' * self.difficulty\n",
    "        return digest.startswith(prefix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 先定义一个区块\n",
    "\n",
    "b = Block(data=\"测试\", prev_hash=\"\")\n",
    "\n",
    "# 再定义一个工作量证明\n",
    "w = ProofOfWork(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.42 s, sys: 2.95 ms, total: 1.42 s\n",
      "Wall time: 1.42 s\n"
     ]
    }
   ],
   "source": [
    "# 进行挖矿，并统计函数执行时间\n",
    "%time  valid_block= w.mine() "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 20 µs, sys: 0 ns, total: 20 µs\n",
      "Wall time: 26 µs\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 验证区块，并计算执行时间\n",
    "%time  w.validate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BlockChain:\n",
    "    \"\"\"\n",
    "        区块链结构体\n",
    "            blocks:        包含的区块列表\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.blocks = []\n",
    "\n",
    "    def add_block(self, block):\n",
    "        \"\"\"\n",
    "        添加区块\n",
    "        \"\"\"\n",
    "        self.blocks.append(block)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "blockchain = BlockChain()\n",
    "\n",
    "new_block1 = Block(data=\"创世区块\", prev_hash=\"\")\n",
    "w1 = ProofOfWork(new_block1)\n",
    "genesis_block = w1.mine()\n",
    "blockchain.add_block(genesis_block)\n",
    "\n",
    "new_block2 = Block(data=\"张三转给李四1个比特币\", prev_hash=genesis_block.hash)\n",
    "w2 = ProofOfWork(new_block2)\n",
    "new_block = w2.mine()\n",
    "blockchain.add_block(new_block)\n",
    "\n",
    "new_block3 = Block(data=\"张三转给王五2个比特币\", prev_hash=new_block.hash)\n",
    "w3 = ProofOfWork(new_block3)\n",
    "new_block = w3.mine()\n",
    "blockchain.add_block(new_block)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "区块链包含区块个数: 3\n",
      "\n",
      "上一个区块哈希：\n",
      "区块内容：创世区块\n",
      "区块哈希：00000d8129023b1740e154e63e820a07b6f603d71f231257fe73e2cfffd2f036\n",
      "\n",
      "\n",
      "上一个区块哈希：00000d8129023b1740e154e63e820a07b6f603d71f231257fe73e2cfffd2f036\n",
      "区块内容：张三转给李四1个比特币\n",
      "区块哈希：00000d38ce6fa764dcd1951e8e6a4ecaa333f0576180f9df4fcd407d9409899c\n",
      "\n",
      "\n",
      "上一个区块哈希：00000d38ce6fa764dcd1951e8e6a4ecaa333f0576180f9df4fcd407d9409899c\n",
      "区块内容：张三转给王五2个比特币\n",
      "区块哈希：00000d302a8304000ce5d0bb3f14af90a5178182b68861c1bab24c9e4f2e189e\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 打印区块链\n",
    "\n",
    "print(\"区块链包含区块个数: %d\\n\" % len(blockchain.blocks))\n",
    "\n",
    "for block in blockchain.blocks:\n",
    "    print(\"上一个区块哈希：%s\" % block.prev_hash)\n",
    "    print(\"区块内容：%s\" % block.data)\n",
    "    print(\"区块哈希：%s\" % block.hash)\n",
    "    print(\"\\n\") "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
